package org.finesys.common.mp.core.encrypt.core.interceptor;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.finesys.common.mp.core.encrypt.core.annotation.EncryptData;
import org.finesys.common.mp.core.encrypt.core.properties.EncryptProperties;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }))
public class DecryptInterceptor implements Interceptor {

	private final EncryptProperties encryptProperties;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object obj = invocation.proceed();
		//
		if (!encryptProperties.isEnableFieldDecrypt()) {
			return obj;
		}

		return this.aesDecrypt(obj);
	}

	private Object aesDecrypt(Object obj) {
		// 查询结果集合
		if (obj instanceof List<?>) {
			return ((ArrayList<?>) obj).stream().peek(o -> {
				if (Objects.nonNull(o)) {
					// 过滤需要解密的字段
					Field[] fields = ReflectUtil.getFields(o.getClass(),
							field -> Objects.nonNull(field.getAnnotation(EncryptData.class)));
					// 解密
					Arrays.asList(fields)
						.stream()
						.forEach(field -> ReflectUtil.setFieldValue(o, field,
								this.aesDecryptValue(ReflectUtil.getFieldValue(o, field))));
				}
			}).collect(Collectors.toList());

		}
		// 查询结果为对象
		Field[] fields = ReflectUtil.getFields(obj.getClass(),
				field -> Objects.nonNull(field.getAnnotation(EncryptData.class)));
		// 解密
		Arrays.asList(fields)
			.stream()
			.forEach(field -> ReflectUtil.setFieldValue(obj, field,
					this.aesDecryptValue(ReflectUtil.getFieldValue(obj, field))));
		return obj;
	}

	private Object aesDecryptValue(Object fieldValue) {
		// 只解密String类型的
		if (fieldValue instanceof String) {
			AES aes = SecureUtil.aes(encryptProperties.getFieldDecryptKey().getBytes(StandardCharsets.UTF_8));
			return new String(aes.decrypt(Base64.decode((String) fieldValue)), StandardCharsets.UTF_8);
		}
		return fieldValue;
	}

}
